﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using KylinORM.Data;
using KylinORM.Data.Parser;
using System.Data;
using System.Text.RegularExpressions;
using KylinORM.Aspect.Definition;
using KylinORM.Data.Parser.Base;

namespace KylinORM.Data.Parser
{
    /// <summary>
    /// 为MapperBase拼装Sql语句
    /// </summary>
    internal abstract class SqlParserBase
    {
        public SqlParserBase(ISqlParserData sqlParserData)
        {
            this.sqlParserData = sqlParserData;
        }

        #region 初始化
        /// <summary>
        /// Sql语法解析器所需的数据
        /// </summary>
        private ISqlParserData sqlParserData;
        private IDataProvider dataProvider = DataProvider.GetInstance();
        /// <summary>
        /// 实体
        /// </summary>
        protected object entity
        {
            get
            {
                return this.sqlParserData.Entity;
            }
        }
        /// <summary>
        /// 实体字段
        /// </summary>
        protected EntityDefinition entityDef
        {
            get
            {
                return this.sqlParserData.EntityDefinition;
            }
        }
        #endregion

        #region 公共模块
        /// <summary>
        /// 得到查询的表名和连接参数
        /// </summary>
        /// <returns></returns>
        protected virtual string GetQueryTable()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendFormat(this.entityDef.EntityInfo.JoinMethod.ToString(),
                this.GetTableName(this.entityDef.EntityInfo.TableName),
                this.entityDef.EntityInfo.JoinTarget);
            return sb.ToString();
        }
        /// <summary>
        /// 得到查询的表名和连接参数
        /// </summary>
        /// <returns></returns>
        protected virtual string GetQueryField()
        {
            StringBuilder sb = new StringBuilder();
            foreach (var kv in this.entityDef.EntityFieldInfos)
            {
                string tableName = kv.Value.TableName ?? this.entityDef.EntityInfo.TableName;
                string fieldName = kv.Value.FieldName;
                //查询字段已显式指定表名，不使用默认表名
                //if (fieldName.IndexOf(".") > 0)
                //{
                //    sb.AppendFormat("{0},", fieldName);
                //}
                //else
                //{
                    sb.AppendFormat("{0}.{1},", this.GetTableName(tableName), this.GetFieldName(fieldName));
                //}
            }
            return sb.ToString().TrimEnd(',');
        }
        /// <summary>
        /// 得到条件语句
        /// </summary>
        /// <returns></returns>
        protected virtual void GetQueryCondition(out string commandText, out object[] parameters)
        {
            StringBuilder sql = new StringBuilder();
            List<object> parms = new List<object>();
            //拼装where条件
            foreach (var kv in this.entityDef.EntityFieldInfos)
            {
                if (kv.Value.GetValue(this.entity) != null)
                {
                    string tableName = kv.Value.TableName ?? this.entityDef.EntityInfo.TableName;
                    string fieldName = kv.Value.FieldName;

                    sql.AppendFormat(kv.Value.SelectMethod + " AND ",
                        this.GetTableName(tableName) + "." + this.GetFieldName(fieldName),
                        this.GetParmName(fieldName));
                    parms.Add(kv.Value.GetValue(this.entity));
                }
            }
            //处理末尾AND
            if (parms.Count > 0)
            {
                sql.Remove(sql.Length - 5, 5);
            }
            else
            {
                sql.Append("1=1");
            }
            //设置参数
            commandText = sql.ToString();
            parameters = parms.ToArray();
        }

        protected abstract string GetTableName(string entityName);

        protected abstract string GetFieldName(string fieldName);

        protected abstract string GetParmName(string fieldName);

        protected abstract DataQuery GetInsertCondition();

        protected abstract DataQuery GetUpdateCondition();

        protected abstract DataQuery GetSelectCondition();

        protected abstract DataQuery GetSelectCondition(string sql, params object[] parms);

        protected abstract DataQuery GetSelectCondition(int skip, int take);

        protected abstract DataQuery GetCountCondition();

        protected abstract DataQuery GetDeleteCondition();

        protected abstract DataQuery[] GetPageCondition(long page, long itemsPerPage, string sql, params object[] args);

        #endregion

        #region CUD模块
        /// <summary>
        /// 得到插入操作
        /// </summary>
        /// <returns></returns>
        internal DataQuery GetInsertCommand()
        {
            StringBuilder sql1 = new StringBuilder(), sql2 = new StringBuilder();
            List<object> parms = new List<object>();
            //拼装where条件
            foreach (var kv in this.entityDef.EntityFieldInfos)
            {
                if (kv.Value.GetValue(this.entity) != null)
                {
                    sql1.AppendFormat("{0},", this.GetFieldName(kv.Value.FieldName));
                    sql2.AppendFormat("{0},", this.GetParmName(kv.Value.FieldName));
                    parms.Add(kv.Value.GetValue(this.entity));
                }
            }
            //处理末尾逗号
            if (parms.Count > 0)
            {
                sql1.Remove(sql1.Length - 1, 1);
                sql2.Remove(sql2.Length - 1, 1);
            }
            //设置参数
            DataQuery query = this.GetInsertCondition();
            query.CommandText = string.Format(query.CommandText,
                this.GetTableName(this.entityDef.EntityInfo.TableName), 
                sql1, 
                sql2);
            query.Parameters = parms.ToArray();
            return query;
        }

        /// <summary>
        /// 得到更新操作
        /// </summary>
        /// <returns></returns>
        internal DataQuery GetUpdateCommand()
        {
            StringBuilder sqlField = new StringBuilder();
            StringBuilder sqlWhere = new StringBuilder();
            List<object> parms = new List<object>();
            
            foreach (var kv in this.entityDef.EntityFieldInfos)
            {
                if (kv.Value.IsPrimaryField != true)
                {
                    //拼装Set条件
                    sqlField.AppendFormat("{0}={1},", this.GetFieldName(kv.Value.FieldName), this.GetParmName(kv.Value.FieldName));
                }
                else
                {
                    //拼装Where条件
                    sqlWhere.AppendFormat("{0}={1} and ", this.GetFieldName(kv.Value.FieldName), this.GetParmName(kv.Value.FieldName));
                }
                parms.Add(this.dataProvider.CreateParameter(this.GetParmName(kv.Value.FieldName), kv.Value.GetValue(this.entity) ?? DBNull.Value));
            }

            if (sqlField.Length > 0)
                sqlField.Remove(sqlField.Length - 1, 1);
            if (sqlWhere.Length > 0)
                sqlWhere.Remove(sqlWhere.Length - 5, 5);            
            //设置参数
            DataQuery query = this.GetUpdateCondition();
            query.CommandText = string.Format(query.CommandText, this.GetTableName(this.entityDef.EntityInfo.TableName), sqlField, sqlWhere);
            query.Parameters = parms.ToArray();
            return query;
        }
        
        /// <summary>
        /// 得到删除操作
        /// </summary>
        /// <returns></returns>
        internal DataQuery GetDeleteCommand()
        {
            //拼装where条件
            string commandText;
            object[] parameters;
            this.GetQueryCondition(out commandText, out parameters);
            //设置参数
            DataQuery query = this.GetDeleteCondition();     
            query.CommandText = string.Format(query.CommandText,
                this.GetTableName(this.entityDef.EntityInfo.TableName),
                commandText);
            query.Parameters = parameters;
            return query;
        }
        #endregion

        #region CQRS模块
        /// <summary>
        /// 得到查询操作
        /// </summary>
        /// <returns></returns>
        internal DataQuery GetSelectCommand()
        {
            //拼装where条件
            string commandText;
            object[] parameters;
            this.GetQueryCondition(out commandText, out parameters);
            //设置参数
            DataQuery query = this.GetSelectCondition();
            query.CommandText = string.Format(query.CommandText, this.GetQueryField(), this.GetQueryTable(), commandText);
            query.Parameters = parameters;
            return query;
        }
        private static Regex rxSelect = new Regex(@"^\s*(SELECT|EXECUTE|CALL)\s", RegexOptions.Compiled | RegexOptions.IgnoreCase);
        private static Regex rxFrom = new Regex(@"\s*FROM\s", RegexOptions.Compiled | RegexOptions.IgnoreCase);
        /// <summary>
        /// 得到指定字段的查询操作
        /// </summary>
        /// <param name="sql">要查询的字段或完整Sql语句</param>
        /// <param name="parms">如果不为null，则替换默认参数</param>
        /// <returns></returns>
        internal DataQuery GetSelectCommand(string sql, params object[] parms)
        {
            if (sql.StartsWith(";"))
            {
                return new DataQuery
                {
                    CommandText = sql.Substring(1),
                    Parameters = parms
                };
            }
            //拼装where条件
            string commandText;
            object[] parameters;
            this.GetQueryCondition(out commandText, out parameters);
            //启用快速查询，只需输入查询字段 SELECT <字段名> FROM 表名
            if (!rxSelect.IsMatch(sql) && !rxFrom.IsMatch(sql))
            {
                DataQuery query = this.GetSelectCondition(sql, parms);
                query.CommandText = string.Format(query.CommandText, sql, this.GetQueryTable(), commandText);
                query.Parameters = (parms == null || parms.Length == 0) ? parameters : parms;
                return query;
            }

            return new DataQuery
            {
                CommandText = sql,
                Parameters = parms
            };
        }
        /// <summary>
        /// 得到行数的查询操作
        /// </summary>
        /// <param name="skip">跳过N行开始获取</param>
        /// <param name="take">总计获取N行</param>
        /// <returns></returns>
        internal DataQuery GetSelectCommand(int skip, int take)
        {
            //拼装where条件
            string commandText;
            object[] parameters;
            this.GetQueryCondition(out commandText, out parameters);
            //设置行数
            DataQuery query = this.GetSelectCondition(skip, take);
            query.CommandText = string.Format(query.CommandText, this.GetQueryField(), this.GetQueryTable(), commandText, skip, take);
            query.Parameters = parameters;
            return query;
        }
        /// <summary>
        /// 得到统计操作
        /// </summary>
        /// <returns></returns>
        internal DataQuery GetCountCommand()
        {
            //拼装where条件
            string commandText;
            object[] parameters;
            this.GetQueryCondition(out commandText, out parameters);
            //设置行数
            DataQuery query = this.GetCountCondition();
            query.CommandText = string.Format(query.CommandText, this.GetQueryTable(), commandText);
            query.Parameters = parameters;
            return query;
        }
        /// <summary>
        /// 得到分页查询操作
        /// </summary>
        /// <param name="page">页码，从1开始</param>
        /// <param name="itemsPerPage">每页行数</param>
        /// <param name="sql">Sql语句</param>
        /// <param name="args">查询参数</param>
        /// <returns>查询语句,统计语句</returns>
        internal DataQuery[] GetPageCommand(long page, long itemsPerPage, string sql, params object[] args)
        {
            DataQuery[] querys = this.GetPageCondition(page, itemsPerPage, sql, args);
            return querys;
        }
        #endregion

    }
}
